#include <2025Linux.h>
typedef struct ConnInfo_s {
    int netfd;
    int isConnected; // 0 未连接，1 已连接
    time_t lastActive; // 这个客户端的上次活跃时间
} ConnInfo_t;
int main(int argc, char *argv[])
{
    // ./1_server_chatroom 192.168.72.128 12345
    ARGS_CHECK(argc,3);
    int sockfd = socket(AF_INET,SOCK_STREAM,0);
    ERROR_CHECK(sockfd,-1,"socket");
    int flag = 1;
    int ret = setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&flag,sizeof(flag));
    ERROR_CHECK(ret,-1,"setsockopt");
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(atoi(argv[2]));
    addr.sin_addr.s_addr = inet_addr(argv[1]);
    ret = bind(sockfd,(struct sockaddr *)&addr, sizeof(addr));
    ERROR_CHECK(ret,-1,"bind");
    ret = listen(sockfd, 10);
    ERROR_CHECK(ret,-1,"listen");

    fd_set monitorset;
    fd_set readyset;
    FD_ZERO(&monitorset);
    FD_SET(sockfd,&monitorset);//最开始的时候只监听sockfd
    // 设计一个数据结构数组，去管理所有的客户端的信息
    ConnInfo_t client[1024];
    for(int i = 0; i < 1024; ++i){
        client[i].isConnected = 0;
    }
    int curidx = 0; //下一个客户端在client数组中的下标
    char buf[4096];
    while(1){
        memcpy(&readyset,&monitorset,sizeof(fd_set));
        struct timeval timeout;
        timeout.tv_sec = 1;
        timeout.tv_usec = 0;
        select(1024,&readyset,NULL,NULL,&timeout);//select就不会修改monitorset的内容了
        time_t now = time(NULL);
        printf("select return, now = %s\n", ctime(&now));
        if(FD_ISSET(sockfd,&readyset)){//有新的客户端接入
            // 往client数组中加了一个成员
            client[curidx].netfd = accept(sockfd,NULL,NULL);//accept之后，sockfd需要继续监听着
            client[curidx].isConnected = 1; 
            client[curidx].lastActive = now; // 记录该连接的活跃时间
            printf("new client is connected! id = %d, netfd = %d\n",curidx, client[curidx].netfd);
            // 把新客户端的netfd加入监听
            FD_SET(client[curidx].netfd, &monitorset);
            // 下标自增
            ++curidx;
        }
        // 某个客户端来消息
        for(int i = 0; i < curidx; ++i){// 0~curidx-1 是所有已经连接的客户端
            if(client[i].isConnected == 1  && FD_ISSET(client[i].netfd, &readyset)){//逻辑与有短路性
                //读消息
                memset(buf,0,sizeof(buf));
                ssize_t sret = recv(client[i].netfd,buf,sizeof(buf),0);
                if(sret == 0){//某个客户端断开了
                    FD_CLR(client[i].netfd,&monitorset);//不监听
                    close(client[i].netfd);
                    client[i].isConnected = 0; //修改client数组内容
                    continue;
                }
                //确实有消息来了
                printf("get message, id = %d, netfd = %d\n",i, client[i].netfd);
                client[i].lastActive = now;
                for(int j = 0; j < curidx; ++j){
                    if(client[j].isConnected == 1 && j != i){//遍历所有其他的存活的client
                        send(client[j].netfd,buf,strlen(buf),0);
                    }
                }
            }
        }//这个循环结束以后，所有的netfd的lastActive该更新的都更新了
        //开始踢人
        for(int i = 0; i < curidx; ++i){
            if(client[i].isConnected == 1 && now - client[i].lastActive > 10){
                FD_CLR(client[i].netfd,&monitorset);//不监听
                close(client[i].netfd); // 断开连接
                client[i].isConnected = 0; //修改client数组内容
            }
        }
    }
    return 0;
}

